package cn.lix.sc.elasticsearch.aop;


import cn.lix.sc.elasticsearch.dto.LoggerInfo;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 特殊接口自己加打印注解{@link WpLogger}
 * @Author 木林森
 * @Date 2021/9/18
 */
@Component
@Aspect
public class WpLoggerAspect {

    private final Logger log = LoggerFactory.getLogger(getClass());

    private AtomicInteger threadNumber = new AtomicInteger(1);

    /**
     * 私有日志打印线程
     */
    private ThreadPoolExecutor executor = new ThreadPoolExecutor(2,
            5,
            3 * 60,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(128),
            task -> {
                Thread t = new Thread(Thread.currentThread().getThreadGroup(),
                        task,
                        "wp-logger-thread-pool-" + threadNumber.getAndIncrement(),
                        0L);
                t.setDaemon(false);
                t.setPriority(Thread.NORM_PRIORITY);
                return t;
            },
            new ThreadPoolExecutor.DiscardPolicy()
    );

    /**
     * 切面
     * @param joinPoint
     * @param logger
     * @return
     * @throws Throwable 不能把异常吃掉，往外抛
     */
    @Around(value = "@annotation(logger)", argNames = "joinPoint,logger")
    public Object printer(ProceedingJoinPoint joinPoint, WpLogger logger) throws Throwable {
        Object obj = null;
        final LoggerInfo loggerInfo = new LoggerInfo();
        long start = System.currentTimeMillis();
        try {
            obj = joinPoint.proceed();
            loggerInfo.setCost(System.currentTimeMillis() - start);
        } catch (Throwable throwable) {
            throw throwable;
        } finally {
            if(logger.output()){
                loggerInfo.setOutput(obj);
            }
            // 异步
            if (logger.async()) {
                executor.execute(() -> {
                    print(logger.level(), transform(joinPoint, loggerInfo, logger));
                });
            }
            // 同步
            else {
                print(logger.level(), transform(joinPoint, loggerInfo, logger));
            }
        }
        return obj;
    }

    /**
     * 转化为输出信息
     * @param joinPoint
     * @param loggerInfo
     * @param logger
     * @return
     */
    private String transform(ProceedingJoinPoint joinPoint, LoggerInfo loggerInfo, WpLogger logger) {
        String string = "";
        Signature signature = joinPoint.getSignature();
        // 为null或不属于MethodSignature不执行
        if (signature == null || !(signature instanceof MethodSignature)) {
            return string;
        }
        try {
            if (StringUtils.isEmpty(logger.mark())) {
                Class<?> targetClass = joinPoint.getTarget().getClass();
                try {
                    // 执行对象
                    MethodSignature methodSignature = (MethodSignature) signature;
                    // 执行方法
                    Method method = targetClass.getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
                    String methodName = targetClass.getName().concat("#").concat(method.getName());
                    loggerInfo.setMark(methodName);
                } catch (NoSuchMethodException e) {
                    loggerInfo.setMark(targetClass.getName());
                }
            } else {
                loggerInfo.setMark(logger.mark());
            }
            Object[] param = handleParam(joinPoint.getArgs());
            loggerInfo.setInput(param);
            string = JSON.toJSONString(loggerInfo);
        } catch (SecurityException e) {
            log.warn("日志打印方法转化参数异常：{}", e.getMessage());
        }
        return string;
    }

    /**
     * 按级别打印
     * @param level
     * @param msg
     */
    private void print(WpLogger.Level level, String msg) {
        switch (level) {
            case DEBUG:
                log.debug(msg);
                return;
            case INFO:
                log.info(msg);
                return;
            case WARN:
                log.warn(msg);
                return;
            case ERROR:
                log.error(msg);
        }
    }

    /**
     * 处理不能输出的参数
     * @param args
     * @return
     */
    private Object[] handleParam(Object[] args) {
        if (args == null || args.length < 1) {
            return new Object[0];
        }
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg instanceof ServletRequest || arg instanceof ServletResponse || arg instanceof InputStreamSource) {
                args[i] = "不可序列化参数占位";
            }
        }
        return args;
    }

}